#pragma once

#include "Error.h"

namespace TestFramework
{
    template <typename T>
    class TestList
    {
    private:
        struct Item
        {
            Item(const T& value)
                : Value(value)
                , Next(0)
            {
            }

            Item(const T& value, Item* next)
                : Value(value)
                , Next(next)
            {
            }

            Item* operator++()
            {
                return this->Next;
            }

            Item* operator++(int)
            {
                Item* tmp = &this;
                operator++();
                return tmp;
            }

            T Value;
            Item* Next;
        };

    public:
        typedef Item* Iterator;
        typedef const Item* ConstIterator;
        typedef size_t SizeType;
        typedef T ValueType;
        typedef const T ConstValueType;

        TestList()
            : Back(0)
            , Front(0)
            , Size(0)
        {
        }

        ~TestList()
        {
            if (Front)
            {
                Iterator tmp;
                do
                {
                    tmp = Front;
                    Front = tmp->Next;
                    delete tmp;
                }
                while (Front);
            }
        }

        ValueType& operator[](SizeType index)
        {
            CheckRange(index);
            return MoveTo(index)->Value;
        }

        void PushBack(const ValueType& value)
        {
            Item* tempNode = new Item(value);
            if (!Front)
            {
                Front = tempNode;
                Back = Front;
            }
            else
            {
                Back->Next = tempNode;
                Back = tempNode;
            }

            ++Size;
        }

        void PushFront(T value)
        {
            Item* tempNode = new Item(value, Front);
            Front = tempNode;

            if (!Back)
            {
                Back = Front;
            }

            ++Size;
        }

        SizeType Insert(SizeType index, ValueType value)
        {
            if (index < 0 || index > Size)
            {
                throw std::exception("Out of range!", Errors::ERROR_OUT_OF_RANGE);
            }

            if (!Front)
            {
                Iterator tempNode = new Item(value);
                Front = tempNode;
                Back = Front;
            }
            else
            {
                Item* item = MoveTo(index);
                Iterator tempNode = new Item(value, item->Next);
                item->Next = tempNode;

                if (!tempNode->Next)
                {
                    Back = tempNode;
                }
            }

            ++Size;
            return index;
        }

        SizeType GetSize() const
        {
            return Size;
        }

        Item* Begin()
        {
            return Front;
        }

        Item* End()
        {
            return Back;
        }

    private:
        Item* MoveTo(SizeType pos) const
        {
            Item* tmp = Front;
            for (SizeType i = pos; i > 0; --i)
            {
                tmp = tmp->Next;
            }

            return tmp;
        }

        void CheckRange(SizeType index) const
        {
            if (index < 0 || index >= Size)
            {
                throw std::exception("Out of range!", Errors::ERROR_OUT_OF_RANGE);
            }
        }

    public:
        Iterator Back;
        Iterator Front;
        SizeType Size;
    };
}
